import os
import json
import time
import curses

from shared.colors import Color
from shared.monitor import Monitor


class DiskMonitor(Monitor):
	"""Monitor that will display free space on specified paths"""
	UNITS = ['B','kB','MB','GB','TB']

	def __init__(self, window, config_file):
		Monitor.__init__(self, window, config_file)

		self._window_size = self._window.getmaxyx()
		self._container = self._window.derwin(self._window_size[0] - 1, self._window_size[1], 1, 0)
		self._container_size = self._container.getmaxyx()

		self._direction = 1
		self._offset = 0
		self._locations = []

	def run(self):
		"""Thread main loop"""
		Monitor.run(self)

		# update data
		self._load_config()
		self._update_data()

		# create header
		self._update_window()

		# enter main loop
		counter = 0
		while self._thread_active.is_set():
			# update domains
			counter += 1
			if counter == 10:
				self._update_data()
				counter = 0

			# update screen
			if self._visible:
				self._update_container()

			# sleep for a while
			time.sleep(1)

	def _format_size(self, size):
		"""Format size according to SI"""
		result = size

		for name in DiskMonitor.UNITS:
			if size < 1000:
				result = '{0:3.1f} {1}'.format(size, name)
				break

			size /= 1000

		return result

	def _load_config(self):
		"""Load locations from config file"""
		decoder = json.JSONDecoder()

		# load JSON and parse it
		if os.path.exists(self._config_file):
			with open(self._config_file, 'r') as locations_data:
				data = decoder.decode(locations_data.read())

		# create domains
		for location in data:
		 	self._locations.append({
		 			'name': location['name'],
					'path': location['path'],
					'free': 0,
					'total': 0
				})

	def _update_data(self):
		"""Update data for all locations"""
		for location in self._locations:
			# get statistics
			try:
				stat = os.statvfs(location['path'])

				# calculate sizes from blocks
				space_free = stat.f_bsize * stat.f_bavail
				space_total = stat.f_bsize * stat.f_blocks

				# update records
				location['free'] = self._format_size(space_free)
				location['total'] = self._format_size(space_total)

			except:
				pass

	def _update_window(self):
		"""Paint window content"""
		self._window.bkgd(curses.color_pair(Color.WINDOW))
		self._container.bkgd(curses.color_pair(Color.CONTAINER))

		# print column names
		self._window.addstr(0, 0, 'Disk', curses.color_pair(Color.WINDOW) | curses.A_BOLD)
		self._window.addstr(0, 25, 'Free', curses.color_pair(Color.HEADER))

		# update container
		self._update_container()

		# mark window as changed
		self._window.noutrefresh()

	def _update_container(self):
		"""Paint window container"""
		sub_data = self._locations[self._offset : self._offset + self._container_size[0]]

		# update offset
		if len(self._locations) > self._container_size[0]:
			self._offset += self._direction
			if self._offset + self._container_size[0] > len(self._locations) and self._direction == 1:
				self._offset = len(self._locations) - self._container_size[0] - 1
				self._direction = -1

			elif self._offset - 1 < 0 and self._direction == -1:
				self._offset = 0
				self._direction = 1

		# draw data
		for index, location in enumerate(sub_data, 0):
			self._container.addstr(index, 0, location['name'].ljust(25))
			self._container.addstr(index, 25, '{0}'.format(location['free']).ljust(10))

		# mark container as updated
		self._container.noutrefresh()

	def show(self):
		"""Show window"""
		Monitor.show(self)
		self._update_window()

	def hide(self):
		"""Hide window"""
		Monitor.hide(self)
